/*-------------------------------------------------------------------------
 *
 * pg_operator.c
 *	  支持pg_operator关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_operator.c
 *
 * NOTES
 *	  这些常规例程从commands/define.c移动到这里并进行了些许整理。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "parser/parse_oper.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"


static Oid	fc_OperatorGet(const char *fc_operatorName,
						Oid fc_operatorNamespace,
						Oid fc_leftObjectId,
						Oid fc_rightObjectId,
						bool *fc_defined);

static Oid	fc_OperatorLookup(List *fc_operatorName,
						   Oid fc_leftObjectId,
						   Oid fc_rightObjectId,
						   bool *fc_defined);

static Oid	fc_OperatorShellMake(const char *fc_operatorName,
							  Oid fc_operatorNamespace,
							  Oid fc_leftTypeId,
							  Oid fc_rightTypeId);

static Oid	get_other_operator(List *fc_otherOp,
							   Oid fc_otherLeftTypeId, Oid fc_otherRightTypeId,
							   const char *fc_operatorName, Oid fc_operatorNamespace,
							   Oid fc_leftTypeId, Oid fc_rightTypeId,
							   bool fc_isCommutator);


/*
 * 检查一个提议的运算符名称是否合法
 *
 * 这最好与 parser/scan.l 的行为相匹配！
 *
 * 我们需要这个，因为解析器没有足够聪明来检查 CREATE OPERATOR 的 COMMUTATOR、NEGATOR 等子句的参数
 * 是运算符名称，而不是其他某种词法实体。
 */
static bool fc_validOperatorName(const char *fc_name)
{
	size_t		fc_len = strlen(fc_name);

	/* 不能为空或太长 */
	if (fc_len == 0 || fc_len >= NAMEDATALEN)
		return false;

	/* 不能包含任何无效字符 */
	
/* 测试字符串应匹配 scan.l 中的 op_chars */
	if (strspn(fc_name, "~!@#^&|`?+-*/%<>=") != fc_len)
		return false;

	/* 不能包含斜线星或破折号破折号（注释开始） */
	if (strstr(fc_name, "/*") || strstr(fc_name, "--"))
		return false;

	/*
	 * For SQL standard compatibility, '+' and '-' cannot be the last char of
	 * a multi-char operator unless the operator contains chars that are not
	 * in SQL operators. The idea is to lex '=-' as two operators, but not to
	 * forbid operator names like '?-' that could not be sequences of standard
	 * SQL operators.
	 */
	if (fc_len > 1 &&
		(fc_name[fc_len - 1] == '+' ||
		 fc_name[fc_len - 1] == '-'))
	{
		int			fc_ic;

		for (fc_ic = fc_len - 2; fc_ic >= 0; fc_ic--)
		{
			if (strchr("~!@#^&|`?%", fc_name[fc_ic]))
				break;
		}
		if (fc_ic < 0)
			return false;		/* 不，无效 */
	}

	/* != 也无效，因为解析器将其转换为 <> */
	if (strcmp(fc_name, "!=") == 0)
		return false;

	return true;
}


/*
 * OperatorGet
 *
 *		根据精确的规格（名称、命名空间、左和右类型 ID）查找操作符。
 *
 *		*defined 被设置为真，如果已定义（不是一个 shell）
 */
static Oid fc_OperatorGet(const char *fc_operatorName,
			Oid fc_operatorNamespace,
			Oid fc_leftObjectId,
			Oid fc_rightObjectId,
			bool *fc_defined)
{
	HeapTuple	fc_tup;
	Oid			fc_operatorObjectId;

	fc_tup = SearchSysCache4(OPERNAMENSP,
						  PointerGetDatum(fc_operatorName),
						  ObjectIdGetDatum(fc_leftObjectId),
						  ObjectIdGetDatum(fc_rightObjectId),
						  ObjectIdGetDatum(fc_operatorNamespace));
	if (HeapTupleIsValid(fc_tup))
	{
		Form_pg_operator fc_oprform = (Form_pg_operator) GETSTRUCT(fc_tup);

		fc_operatorObjectId = fc_oprform->oid;
		*fc_defined = RegProcedureIsValid(fc_oprform->oprcode);
		ReleaseSysCache(fc_tup);
	}
	else
	{
		fc_operatorObjectId = InvalidOid;
		*fc_defined = false;
	}

	return fc_operatorObjectId;
}

/*
 * OperatorLookup
 *
 *		根据可能合格的名称和左、右类型 ID 查找操作符。
 *
 *		*defined 被设置为真，如果已定义（不是一个 shell）
 */
static Oid fc_OperatorLookup(List *fc_operatorName,
			   Oid fc_leftObjectId,
			   Oid fc_rightObjectId,
			   bool *fc_defined)
{
	Oid			fc_operatorObjectId;
	RegProcedure fc_oprcode;

	fc_operatorObjectId = LookupOperName(NULL, fc_operatorName,
									  fc_leftObjectId, fc_rightObjectId,
									  true, -1);
	if (!OidIsValid(fc_operatorObjectId))
	{
		*fc_defined = false;
		return InvalidOid;
	}

	fc_oprcode = get_opcode(fc_operatorObjectId);
	*fc_defined = RegProcedureIsValid(fc_oprcode);

	return fc_operatorObjectId;
}


/*
 * OperatorShellMake
 *		为一个尚不存在的操作符创建一个 "shell" 条目。
 */
static Oid fc_OperatorShellMake(const char *fc_operatorName,
				  Oid fc_operatorNamespace,
				  Oid fc_leftTypeId,
				  Oid fc_rightTypeId)
{
	Relation	pg_operator_desc;
	Oid			fc_operatorObjectId;
	int			fc_i;
	HeapTuple	fc_tup;
	Datum		fc_values[Natts_pg_operator];
	bool		fc_nulls[Natts_pg_operator];
	NameData	fc_oname;
	TupleDesc	fc_tupDesc;

	/*
	 * 验证操作符名称
	 */
	if (!fc_validOperatorName(fc_operatorName))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_NAME),
				 errmsg("\"%s\" is not a valid operator name",
						fc_operatorName)));

	/*
	 * 打开 pg_operator
	 */
	pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);
	fc_tupDesc = pg_operator_desc->rd_att;

	/*
	 * 初始化我们的 *nulls 和 *values 数组
	 */
	for (fc_i = 0; fc_i < Natts_pg_operator; ++fc_i)
	{
		fc_nulls[fc_i] = false;
		fc_values[fc_i] = (Datum) NULL;	/* 多余，但安全 */
	}

	/*
	 * 用操作符名称和输入数据类型初始化 values[]。注意 oprcode 被设置为 InvalidOid，表示它是一个 shell。
	 */
	fc_operatorObjectId = GetNewOidWithIndex(pg_operator_desc, OperatorOidIndexId,
										  Anum_pg_operator_oid);
	fc_values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(fc_operatorObjectId);
	namestrcpy(&fc_oname, fc_operatorName);
	fc_values[Anum_pg_operator_oprname - 1] = NameGetDatum(&fc_oname);
	fc_values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(fc_operatorNamespace);
	fc_values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
	fc_values[Anum_pg_operator_oprkind - 1] = CharGetDatum(fc_leftTypeId ? 'b' : 'l');
	fc_values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false);
	fc_values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
	fc_values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(fc_leftTypeId);
	fc_values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(fc_rightTypeId);
	fc_values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);

	/*
	 * 创建一个新的操作符元组
	 */
	fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);

	/*
	 * 插入我们的 "shell" 操作符元组
	 */
	CatalogTupleInsert(pg_operator_desc, fc_tup);

	/* 为条目添加依赖关系 */
	makeOperatorDependencies(fc_tup, true, false);

	heap_freetuple(fc_tup);

	/* 新 shell 操作符的创建后钩子 */
	InvokeObjectPostCreateHook(OperatorRelationId, fc_operatorObjectId, 0);

	/*
	 * 确保元组对后续查找/更新可见。
	 */
	CommandCounterIncrement();

	/*
	 * 关闭操作符关系并返回 oid。
	 */
	table_close(pg_operator_desc, RowExclusiveLock);

	return fc_operatorObjectId;
}

/*
 * OperatorCreate
 *
 * "X" 表示可选参数（即可以是 NULL 或 0 的参数）
 *		operatorName			新操作符的名称
 *		operatorNamespace		新操作符的命名空间
 *		leftTypeId				X 左类型 ID
 *		rightTypeId				X 右类型 ID
 *		procedureId				操作符的过程 ID
 *		commutatorName			X 交换操作符
 *		negatorName				X 取反操作符
 *		restrictionId			X 限制选择性过程 ID
 *		joinId					X 连接选择性过程 ID
 *		canMerge				合并连接可以与此操作符一起使用
 *		canHash					哈希连接可以与此操作符一起使用
 *
 * 调用者应验证作为 OID 引用传递的对象的属性和权限。然而，我们必须特别处理交换和取反操作符的引用，因为这些可能事先不存在。
 *
 * 这个程序比较复杂，因为它允许用户指定不存在的操作符。例如，如果正在定义操作符 "op"，则可以定义取反操作符 "negop" 和交换操作符 "commop"，而不需要指定任何信息，只需提供其名称即可。由于为了将 "op" 添加到 PG_OPERATOR 目录，所有这些操作符的 Oid 必须放在 "op" 的字段中，因此对交换和取反操作符进行了前向声明。这被称为创建一个 shell，它的主要作用是在 PG_OPERATOR 目录中创建一个包含最少操作符信息（仅名称和类型）的元组。前向声明仅用于此目的，它并不对用户开放，就像类型定义一样。
 */
ObjectAddress OperatorCreate(const char *fc_operatorName,
			   Oid fc_operatorNamespace,
			   Oid fc_leftTypeId,
			   Oid fc_rightTypeId,
			   Oid fc_procedureId,
			   List *fc_commutatorName,
			   List *fc_negatorName,
			   Oid fc_restrictionId,
			   Oid fc_joinId,
			   bool fc_canMerge,
			   bool fc_canHash)
{
	Relation	pg_operator_desc;
	HeapTuple	fc_tup;
	bool		fc_isUpdate;
	bool		fc_nulls[Natts_pg_operator];
	bool		fc_replaces[Natts_pg_operator];
	Datum		fc_values[Natts_pg_operator];
	Oid			fc_operatorObjectId;
	bool		fc_operatorAlreadyDefined;
	Oid			fc_operResultType;
	Oid			fc_commutatorId,
				fc_negatorId;
	bool		fc_selfCommutator = false;
	NameData	fc_oname;
	int			fc_i;
	ObjectAddress fc_address;

	/*
	 * 合理性检查
	 */
	if (!fc_validOperatorName(fc_operatorName))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_NAME),
				 errmsg("\"%s\" is not a valid operator name",
						fc_operatorName)));

	if (!(OidIsValid(fc_leftTypeId) && OidIsValid(fc_rightTypeId)))
	{
		/* 如果它不是二元操作，这些内容不能被设置： */
		if (fc_commutatorName)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only binary operators can have commutators")));
		if (OidIsValid(fc_joinId))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only binary operators can have join selectivity")));
		if (fc_canMerge)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only binary operators can merge join")));
		if (fc_canHash)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only binary operators can hash")));
	}

	fc_operResultType = get_func_rettype(fc_procedureId);

	if (fc_operResultType != BOOLOID)
	{
		/* 如果它不是布尔操作符，则这些项必须未设置： */
		if (fc_negatorName)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have negators")));
		if (OidIsValid(fc_restrictionId))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have restriction selectivity")));
		if (OidIsValid(fc_joinId))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can have join selectivity")));
		if (fc_canMerge)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can merge join")));
		if (fc_canHash)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only boolean operators can hash")));
	}

	fc_operatorObjectId = fc_OperatorGet(fc_operatorName,
								   fc_operatorNamespace,
								   fc_leftTypeId,
								   fc_rightTypeId,
								   &fc_operatorAlreadyDefined);

	if (fc_operatorAlreadyDefined)
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_FUNCTION),
				 errmsg("operator %s already exists",
						fc_operatorName)));

	/*
	 * 在这一点上，如果 operatorObjectId 不是 InvalidOid，则我们正在填充一个先前创建的 shell。坚持要求用户拥有任何这样的 shell。
	 */
	if (OidIsValid(fc_operatorObjectId) &&
		!pg_oper_ownercheck(fc_operatorObjectId, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
					   fc_operatorName);

	/*
	 * 设置其他操作符。如果它们当前不存在，则创建 shell 以获取 ObjectId。
	 */

	if (fc_commutatorName)
	{
		/* 交换操作符具有反转的参数类型 */
		fc_commutatorId = get_other_operator(fc_commutatorName,
										  fc_rightTypeId, fc_leftTypeId,
										  fc_operatorName, fc_operatorNamespace,
										  fc_leftTypeId, fc_rightTypeId,
										  true);

		/* 权限检查：必须拥有其他操作符 */
		if (OidIsValid(fc_commutatorId) &&
			!pg_oper_ownercheck(fc_commutatorId, GetUserId()))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
						   NameListToString(fc_commutatorName));

		/*
		 * 自我链接到这个操作符；将在下面修复。注意，只有交换的自我链接才有意义。
		 */
		if (!OidIsValid(fc_commutatorId))
			fc_selfCommutator = true;
	}
	else
		fc_commutatorId = InvalidOid;

	if (fc_negatorName)
	{
		/* 取反操作符具有相同的参数类型 */
		fc_negatorId = get_other_operator(fc_negatorName,
									   fc_leftTypeId, fc_rightTypeId,
									   fc_operatorName, fc_operatorNamespace,
									   fc_leftTypeId, fc_rightTypeId,
									   false);

		/* 权限检查：必须拥有其他操作符 */
		if (OidIsValid(fc_negatorId) &&
			!pg_oper_ownercheck(fc_negatorId, GetUserId()))
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
						   NameListToString(fc_negatorName));
	}
	else
		fc_negatorId = InvalidOid;

	/*
	 * 设置操作符元组中的值
	 */

	for (fc_i = 0; fc_i < Natts_pg_operator; ++fc_i)
	{
		fc_values[fc_i] = (Datum) NULL;
		fc_replaces[fc_i] = true;
		fc_nulls[fc_i] = false;
	}

	namestrcpy(&fc_oname, fc_operatorName);
	fc_values[Anum_pg_operator_oprname - 1] = NameGetDatum(&fc_oname);
	fc_values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(fc_operatorNamespace);
	fc_values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
	fc_values[Anum_pg_operator_oprkind - 1] = CharGetDatum(fc_leftTypeId ? 'b' : 'l');
	fc_values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(fc_canMerge);
	fc_values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(fc_canHash);
	fc_values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(fc_leftTypeId);
	fc_values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(fc_rightTypeId);
	fc_values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(fc_operResultType);
	fc_values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(fc_commutatorId);
	fc_values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(fc_negatorId);
	fc_values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(fc_procedureId);
	fc_values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(fc_restrictionId);
	fc_values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(fc_joinId);

	pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);

	/*
	 * 如果我们正在替换操作符 shell，则更新；否则插入
	 */
	if (fc_operatorObjectId)
	{
		fc_isUpdate = true;

		fc_tup = SearchSysCacheCopy1(OPEROID,
								  ObjectIdGetDatum(fc_operatorObjectId));
		if (!HeapTupleIsValid(fc_tup))
			elog(ERROR, "cache lookup failed for operator %u",
				 fc_operatorObjectId);

		fc_replaces[Anum_pg_operator_oid - 1] = false;
		fc_tup = heap_modify_tuple(fc_tup,
								RelationGetDescr(pg_operator_desc),
								fc_values,
								fc_nulls,
								fc_replaces);

		CatalogTupleUpdate(pg_operator_desc, &fc_tup->t_self, fc_tup);
	}
	else
	{
		fc_isUpdate = false;

		fc_operatorObjectId = GetNewOidWithIndex(pg_operator_desc,
											  OperatorOidIndexId,
											  Anum_pg_operator_oid);
		fc_values[Anum_pg_operator_oid - 1] = ObjectIdGetDatum(fc_operatorObjectId);

		fc_tup = heap_form_tuple(RelationGetDescr(pg_operator_desc),
							  fc_values, fc_nulls);

		CatalogTupleInsert(pg_operator_desc, fc_tup);
	}

	/* 为条目添加依赖关系 */
	fc_address = makeOperatorDependencies(fc_tup, true, fc_isUpdate);

	/* 新操作符的后创建钩子 */
	InvokeObjectPostCreateHook(OperatorRelationId, fc_operatorObjectId, 0);

	table_close(pg_operator_desc, RowExclusiveLock);

	/*
	 * 如果提供了对易位符和/或取反符的链接，则更新其他操作符
	 * 以指向这个操作符，如果它们还没有链接的话。
	 * 这支持一种替代的操作符定义风格，其中用户首先定义一个操作符而不提供取反符或易位符，
	 * 然后用适当的易位符或取反符属性定义这个对的另一个操作符。这种风格不需要创建外壳，
	 * 而且这是在Postgres版本6.5之前唯一正确工作的风格。
	 * 这段代码还处理了新操作符是其自身的易位符的情况。
	 */
	if (fc_selfCommutator)
		fc_commutatorId = fc_operatorObjectId;

	if (OidIsValid(fc_commutatorId) || OidIsValid(fc_negatorId))
		OperatorUpd(fc_operatorObjectId, fc_commutatorId, fc_negatorId, false);

	return fc_address;
}

/*
 * 尝试查找另一个操作符（易位符等）
 *
 * 如果没有找到，检查是否与我们尝试定义的操作符完全相同；如果是，返回InvalidOid。
 * （请注意，这种情况仅对易位符才有意义，因此在其他情况下我们会报错。）如果不是
 * 同一操作符，则创建一个外壳操作符。
 */
static Oid get_other_operator(List *fc_otherOp, Oid fc_otherLeftTypeId, Oid fc_otherRightTypeId,
				   const char *fc_operatorName, Oid fc_operatorNamespace,
				   Oid fc_leftTypeId, Oid fc_rightTypeId, bool fc_isCommutator)
{
	Oid			fc_other_oid;
	bool		fc_otherDefined;
	char	   *fc_otherName;
	Oid			fc_otherNamespace;
	AclResult	fc_aclresult;

	fc_other_oid = fc_OperatorLookup(fc_otherOp,
							   fc_otherLeftTypeId,
							   fc_otherRightTypeId,
							   &fc_otherDefined);

	if (OidIsValid(fc_other_oid))
	{
		/* 其他操作符已在目录中 */
		return fc_other_oid;
	}

	fc_otherNamespace = QualifiedNameGetCreationNamespace(fc_otherOp,
													   &fc_otherName);

	if (strcmp(fc_otherName, fc_operatorName) == 0 &&
		fc_otherNamespace == fc_operatorNamespace &&
		fc_otherLeftTypeId == fc_leftTypeId &&
		fc_otherRightTypeId == fc_rightTypeId)
	{
		/*
		 * 自我链接到这个操作符；调用者稍后会修复。请注意，
		 * 只有易位符的自我链接才有意义。
		 */
		if (!fc_isCommutator)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("operator cannot be its own negator or sort operator")));
		return InvalidOid;
	}

	/* 不在目录中，与操作符不同，因此制作外壳 */

	fc_aclresult = pg_namespace_aclcheck(fc_otherNamespace, GetUserId(),
									  ACL_CREATE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA,
					   get_namespace_name(fc_otherNamespace));

	fc_other_oid = fc_OperatorShellMake(fc_otherName,
								  fc_otherNamespace,
								  fc_otherLeftTypeId,
								  fc_otherRightTypeId);
	return fc_other_oid;
}

/*
 * OperatorUpd
 *
 *	对于给定的操作符，查找其取反符和易位符操作符。
 *	当isDelete为false时，更新它们的取反符和易位符字段以指向给定操作符；
 *	当isDelete为true时，更新这些字段以不再指向给定操作符。
 *
 *	!isDelete情况解决了需要插入两个新操作符的问题，这两个操作符是彼此的取反符或易位符，
 *	而isDelete情况是为了在删除操作符后不留下悬挂的OID链接。
 */
void OperatorUpd(Oid fc_baseId, Oid fc_commId, Oid fc_negId, bool fc_isDelete)
{
	Relation	pg_operator_desc;
	HeapTuple	fc_tup;

	/*
	 * 如果我们将一个操作符变为其自身的易位符，那么我们需要在这里进行
	 * 命令计数器的递增，因为我们刚刚插入了即将更新的元组。
	 * 但当我们删除一个操作符时，可以跳过这一点，因为我们处于命令的开始。
	 */
	if (!fc_isDelete)
		CommandCounterIncrement();

	/* 打开关系。 */
	pg_operator_desc = table_open(OperatorRelationId, RowExclusiveLock);

	/* 获取易位符元组的可写副本。 */
	if (OidIsValid(fc_commId))
		fc_tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(fc_commId));
	else
		fc_tup = NULL;

	/* 如有需要，更新易位符元组。 */
	if (HeapTupleIsValid(fc_tup))
	{
		Form_pg_operator fc_t = (Form_pg_operator) GETSTRUCT(fc_tup);
		bool		update_commutator = false;

		/*
		 * 出于谨慎考虑，我们仅在易位符的oprcom字段具有我们预期的确切值时更改它：
		 * 创建操作符时为InvalidOid，或删除操作符时为baseId。
		 */
		if (fc_isDelete && fc_t->oprcom == fc_baseId)
		{
			fc_t->oprcom = InvalidOid;
			update_commutator = true;
		}
		else if (!fc_isDelete && !OidIsValid(fc_t->oprcom))
		{
			fc_t->oprcom = fc_baseId;
			update_commutator = true;
		}

		/* 如果发现需要修改的列，则更新元组。 */
		if (update_commutator)
		{
			CatalogTupleUpdate(pg_operator_desc, &fc_tup->t_self, fc_tup);

			/*
			 * 执行CCI以使更新后的元组可见。
			 * 我们必须这样做，以防易位符也是取反符。
			 * （这在操作符定义者的逻辑上将是一个错误，但这并不是失败的好理由。）
			 * 在删除无取反符的自我易位符的情况下，我们无论如何都需要一个CCI。
			 */
			CommandCounterIncrement();
		}
	}

	/*
	 * 同样找到并更新取反符（如果有的话）。
	 */
	if (OidIsValid(fc_negId))
		fc_tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(fc_negId));
	else
		fc_tup = NULL;

	if (HeapTupleIsValid(fc_tup))
	{
		Form_pg_operator fc_t = (Form_pg_operator) GETSTRUCT(fc_tup);
		bool		update_negator = false;

		/*
		 * 出于谨慎考虑，我们仅在取反符的oprnegate字段具有我们预期的确切值时更改它：
		 * 创建操作符时为InvalidOid，或删除操作符时为baseId。
		 */
		if (fc_isDelete && fc_t->oprnegate == fc_baseId)
		{
			fc_t->oprnegate = InvalidOid;
			update_negator = true;
		}
		else if (!fc_isDelete && !OidIsValid(fc_t->oprnegate))
		{
			fc_t->oprnegate = fc_baseId;
			update_negator = true;
		}

		/* 如果发现需要修改的列，则更新元组。 */
		if (update_negator)
		{
			CatalogTupleUpdate(pg_operator_desc, &fc_tup->t_self, fc_tup);

			/*
			 * 在删除情况下，执行CCI以使更新后的元组可见。
			 * 我们必须这样做，以防操作符是其自身的取反符。
			 * （这在操作符定义者的逻辑上将是一个错误，但这并不是失败的好理由。）
			 */
			if (fc_isDelete)
				CommandCounterIncrement();
		}
	}

	/* 关闭关系并释放目录锁。 */
	table_close(pg_operator_desc, RowExclusiveLock);
}

/*
 * 为一个操作符创建依赖关系（无论是新插入的完整操作符，
 * 新的外壳操作符，刚更新的外壳，
 * 还是通过ALTER OPERATOR正在修改的操作符）。
 *
 * makeExtensionDep在创建新操作符或替换外壳操作符时应为true，
 * 对于ALTER OPERATOR则为false。传递false将防止操作符的扩展成员资格发生变化。
 *
 * 注意：此例程中OidIsValid测试是必要的，以防给定操作符是一个外壳。
 */
ObjectAddress makeOperatorDependencies(HeapTuple fc_tuple,
						 bool fc_makeExtensionDep,
						 bool fc_isUpdate)
{
	Form_pg_operator fc_oper = (Form_pg_operator) GETSTRUCT(fc_tuple);
	ObjectAddress fc_myself,
				fc_referenced;
	ObjectAddresses *fc_addrs;

	ObjectAddressSet(fc_myself, OperatorRelationId, fc_oper->oid);

	/*
	 * 如果我们正在更新操作符，则删除任何现有条目，除了
	 * 扩展成员资格应保持不变。
	 */
	if (fc_isUpdate)
	{
		deleteDependencyRecordsFor(fc_myself.classId, fc_myself.objectId, true);
		deleteSharedDependencyRecordsFor(fc_myself.classId, fc_myself.objectId, 0);
	}

	fc_addrs = new_object_addresses();

	/* 对命名空间的依赖关系 */
	if (OidIsValid(fc_oper->oprnamespace))
	{
		ObjectAddressSet(fc_referenced, NamespaceRelationId, fc_oper->oprnamespace);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 对左类型的依赖关系 */
	if (OidIsValid(fc_oper->oprleft))
	{
		ObjectAddressSet(fc_referenced, TypeRelationId, fc_oper->oprleft);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 对右类型的依赖关系 */
	if (OidIsValid(fc_oper->oprright))
	{
		ObjectAddressSet(fc_referenced, TypeRelationId, fc_oper->oprright);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 对结果类型的依赖关系 */
	if (OidIsValid(fc_oper->oprresult))
	{
		ObjectAddressSet(fc_referenced, TypeRelationId, fc_oper->oprresult);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/*
	 * 注意：我们不将操作符视为依赖于相关的
	 * 操作符oprcom和oprnegate。如果那些消失了，我们不想删除该
	 * 操作符，而只是重置链接字段；这不是当前依赖代码可以处理的功能。
	 * （或许可以通过objectSubId做一些事情。）
	 * 现在，如果引用的操作符被删除，允许那些链接悬挂是可以的。
	 */

	/* 对实现函数的依赖关系 */
	if (OidIsValid(fc_oper->oprcode))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_oper->oprcode);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 对限制选择性函数的依赖关系 */
	if (OidIsValid(fc_oper->oprrest))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_oper->oprrest);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	/* 对联接选择性函数的依赖关系 */
	if (OidIsValid(fc_oper->oprjoin))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_oper->oprjoin);
		add_exact_object_address(&fc_referenced, fc_addrs);
	}

	record_object_address_dependencies(&fc_myself, fc_addrs, DEPENDENCY_NORMAL);
	free_object_addresses(fc_addrs);

	
/* 对于所有者的依赖 */
	recordDependencyOnOwner(OperatorRelationId, fc_oper->oid,
							fc_oper->oprowner);

	/* 对于扩展的依赖 */
	if (fc_makeExtensionDep)
		recordDependencyOnCurrentExtension(&fc_myself, fc_isUpdate);

	return fc_myself;
}
